home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga News 95
/
Amiga News 95.iso
/
dpat
/
dpat106
/
safefunction
/
source
/
safefunction.s
< prev
next >
Wrap
Text File
|
1995-07-21
|
12KB
|
482 lines
; SafeFunction.s
; This program patches the SetFunction() function. This tries to solve problems
; when two programs try to patch the same function (the system may crash if
; the programs remove their patch in the wrong order).
; This version is based on the program named SaferPatches which was placed in
; the public domain by the author:
; Martin Adrian
; Rullharvsgatan 3A
; S-431 47 Mölndal
; SWEDEN
; This version was converted to assembly language for best efficiency, and tries
; to handle better memory failures: SetFunction() is not allowed to fail, but I
; have to make some memory allocations. The problem is solved by throwing an
; "emergency block" into the free memory list on memory shortage. Moreover, all
; allocations are done with pooled allocation functions, in order to reduce
; memory fragmentation. Author:
; Frédéric Delacroix
; 5 rue d'Artres
; 59269 Quérénaing
; FRANCE
; This program and the source is fully public domain.
; version 1.0: Assembly version of Martin Adrian's program.
; version 1.1: semaphore added to public list for easy rendez-vous.
; No longer startable from Workbench (the best place is
; right after SetPatch in the startup-sequence).
; version 1.2: Enclosed critical parts in Disable()/Enable() (in case
; an interrupt would try to execute a function whose patch
; is not yet correctly installed). Now clears the CPU caches.
; version 1.3: Added a low-memory handler to throw an emergency block of
; free memory into the system pool on a memory allocation
; failure during SetFunction(). This reduces the threat of
; an unexpected NULL return value from SetFunction()...
; version 1.4: All memory allocations are now done via pools, in order to
; reduce memory fragmentation.
; version 1.5: Some minor guide updates, wrote a french documentation.
VERSION MACRO
dc.b '1.5'
ENDM
section Startup,CODE
EMERGENCY_BLOCKSIZE EQU 32
MEMPUDDLE_SIZE EQU 32
Start move.l 4.w,a6
lea Variables,a5
move.l #20,d7 ; return code
lea DOS.Name(pc),a1
moveq #39,d0 ; kickstart >= 3.0 required
jsr _LVOOpenLibrary(a6)
move.l d0,a4
move.l a4,d0
beq NoDOS
move.l #Bienvenue.MSG,d1
exg.l a4,a6
jsr _LVOPutStr(a6)
exg.l a4,a6
jsr _LVOForbid(a6)
lea Semaphore.Name,a1
jsr _LVOFindSemaphore(a6)
tst.l d0
bne AlreadyInstalled
move.l #MEMF_PUBLIC!MEMF_CLEAR,d0
move.l #MEMPUDDLE_SIZE,d1
move.l d1,d2
jsr _LVOCreatePool(a6)
move.l a0,var_MemoryPool(a5)
beq NoPool
suba.l a0,a0
jsr MakeHead
move.l d0,var_MainHeader(a5)
beq.s NoMemory
move.l #_LVOSetFunction,a0
move.l #NewSetFunction,d0
move.l a6,a1
jsr _LVOSetFunction(a6)
move.l d0,var_OldSetFunc(a5)
beq.s NoSetFunction
lea LowMemHandler.Interrupt,a1
jsr _LVOAddMemHandler(a6)
move.l #EMERGENCY_BLOCKSIZE,d0
move.l #MEMF_PUBLIC,d1
jsr _LVOAllocMem(a6)
move.l d0,var_EmergencyBlock(a5)
beq.s .NoFlag
bset #SFB_BLOCKAVAILABLE,var_Flags(a5)
.NoFlag lea var_SetFuncSemaphore(a5),a1
move.l #Semaphore.Name,LN_NAME(a1)
jsr _LVOAddSemaphore(a6)
jsr _LVOPermit(a6)
move.l #Installed.MSG,d1
exg.l a4,a6
jsr _LVOPutStr(a6)
exg.l a6,a4
; cut the seglist
lea Start-4(pc),a0
clr.l (a0) ; tchac
moveq #0,d7
CloseDOS
move.l a4,a1
jsr _LVOCloseLibrary(a6)
NoDOS move.l d7,d0
rts
NoSetFunction
move.l var_MainHeader(a5),a1
move.l #libh_SIZEOF,d0
jsr FreeMemory
NoMemory
move.l var_MemoryPool(a5),a0
jsr _LVODeletePool(a6)
NoPool move.l #Failed.MSG,d1
PrintAndGo
exg.l a4,a6
jsr _LVOPutStr(a6)
exg.l a4,a6
bra.s CloseDOS
AlreadyInstalled
move.l #AlreadyInstalled.MSG,d1
moveq #5,d7
bra.s PrintAndGo
DOS.Name dc.b 'dos.library',0
Bienvenue.MSG dc.b 'SafeFunction '
VERSION
dc.b ' by F.Delacroix, public domain.',10,0
Installed.MSG dc.b ' Patch successfully installed',10,0
Failed.MSG dc.b ' Failed to install patch ! (not enough memory ?!)',10,0
AlreadyInstalled.MSG dc.b ' Patch already active (you cannot remove it).',10,0
section ResidentPatch,CODE
STRUCTURE LibHeader,0
APTR libh_LibBase ; points to patched library
APTR libh_Patches ; points to list of patches
APTR libh_NextLib ; points to next library header
LABEL libh_SIZEOF
STRUCTURE LibPatch,0
UWORD libp_JMP ; JMP instruction
APTR libp_OldFunc ; old (before patch) function
APTR libp_NewFunc ; new (after patch) function
WORD libp_Offset ; offset in jump table
APTR libp_NextPatch ; points to next patch in library
LABEL libp_SIZEOF
MAGIC_OFFSET EQU $2357 ; prime numbers :-]
MakeHead ; (D0)LibHeader=MakeHead(Library)(A0)
movem.l d1/a0-a2,-(sp)
move.l a0,a2
move.l #libh_SIZEOF,d0
bsr AllocMemory
move.l d0,a0
move.l a0,d0
beq.s .Fail
move.l a2,libh_LibBase(a0)
.Fail movem.l (sp)+,d1/a0-a2
rts
MakePatch ; (D0)Patches=MakePatch(Offset,Next,NewFunc)(D0,A0,A1)
movem.l d1-d2/a0-a3,-(sp)
move.l d0,d2
move.l a0,a2
move.l a1,a3
move.l #libp_SIZEOF,d0
bsr AllocMemory
move.l d0,a0
move.l a0,d0
beq.s .Fail
move.w #$4EF9,libp_JMP(a0) ; opcode for JMP
move.l a3,libp_NewFunc(a0)
move.w d2,libp_Offset(a0)
move.l a2,libp_NextPatch(a0)
.Fail movem.l (sp)+,d1-d2/a0-a3
rts
SF_OFFSET EQUR d7
SF_LIBRARY EQUR d6
SF_NEWFUNC EQUR d5
SF_OLDFUNC EQUR d4
OldSetFunction ; (D0)OldFunc=OldSetFunction(NewFunc)(A0)
move.w SF_OFFSET,d0
ext.l d0
exg.l d0,a0
move.l SF_LIBRARY,a1
move.l var_OldSetFunc(a5),-(sp)
rts
NewSetFunction ; (D0)OldFunc=NewSetFunction(Offset,Library,NewFunc)(A0,A1,D0)
movem.l a0-a3/a5/d1/d4-d7,-(sp)
lea Variables,a5
moveq #0,SF_OLDFUNC
move.w a0,SF_OFFSET
move.l a1,SF_LIBRARY
move.l d0,SF_NEWFUNC
lea var_SetFuncSemaphore(a5),a0
jsr _LVOObtainSemaphore(a6)
move.l var_MainHeader(a5),a0
.NextLib
move.l libh_NextLib(a0),d0
beq .NoMoreLib
move.l d0,a1
cmp.l libh_LibBase(a1),SF_LIBRARY
beq.s .LibFound
move.l a1,a0
bra .NextLib
.LibFound
move.l a0,var_HeadPtr(a5)
move.l a1,var_HeadPtr1(a5)
move.l libh_Patches(a1),var_Patch(a5)
.NextPatch
move.l var_Patch(a5),a0
move.l libp_NextPatch(a0),a1
move.l a1,var_Patch1(a5)
beq.s .MakeANewPatch
cmp.w libp_Offset(a1),SF_OFFSET
ble.s .SearchPatchOn
; make a new patch (none installed here)
.MakeANewPatch
move.w SF_OFFSET,d0
move.l var_Patch1(a5),a0
move.l SF_NEWFUNC,a1
bsr MakePatch
move.l d0,var_Patch1(a5)
beq .Done
move.l SF_NEWFUNC,a0
jsr _LVODisable(a6)
bsr OldSetFunction
move.l var_Patch1(a5),a0
move.l d0,libp_OldFunc(a0)
jsr _LVOEnable(a6)
tst.l d0
beq.s .OldSetFuncFailed ; (should not happen, but let's be careful)
move.l var_Patch(a5),a0
move.l var_Patch1(a5),libp_NextPatch(a0)
move.l var_Patch1(a5),SF_OLDFUNC
bra .Done
.OldSetFuncFailed
move.l var_Patch1(a5),a1
move.l #libp_SIZEOF,d0
bsr FreeMemory
bra .Done
.SearchPatchOn
move.l var_Patch1(a5),a0
cmp.w libp_Offset(a0),SF_OFFSET
bne .NotThisPatch
cmp.l a0,SF_NEWFUNC
beq.s .RemoveLast
cmp.l libp_OldFunc(a0),SF_NEWFUNC
bne.s .NotRemoveLast
; user wants to remove the last patch
.RemoveLast
move.l libp_OldFunc(a0),a0
bsr OldSetFunction ; no need for Disable() here: let SetFunction() do it
move.l d0,SF_OLDFUNC
move.l var_Patch1(a5),a0
cmp.l libp_NewFunc(a0),SF_OLDFUNC
bne.s .BigTrouble
move.l var_Patch1(a5),a1
move.l var_Patch(a5),a0
move.l libp_NextPatch(a1),libp_NextPatch(a0)
move.l #libp_SIZEOF,d0
bsr FreeMemory
bra.s .LastRemoved
.BigTrouble ; if execution arrives here, than someone has changed
move.l SF_OLDFUNC,a0 ; the vector from the outside...
move.l a0,SF_OLDFUNC ; we are in BIG TROUBLE, we just restore
beq .Done ; the original vector and pray.
bsr OldSetFunction
moveq #0,SF_OLDFUNC
bra .Done
.LastRemoved
move.l var_Patch(a5),a0
cmp.w #MAGIC_OFFSET,libp_Offset(a0)
bne .Done
tst.l libp_NextPatch(a0)
bne .Done
; remove unused library header
move.l var_HeadPtr(a5),a0
move.l var_HeadPtr1(a5),a1
move.l libh_NextLib(a1),libh_NextLib(a0)
move.l #libh_SIZEOF,d0
bsr FreeMemory
move.l var_Patch(a5),a1
move.l #libp_SIZEOF,d0
bsr FreeMemory
bra .Done
.NotRemoveLast
move.l var_Patch1(a5),a0
.RemoveLoop
move.l libp_NextPatch(a0),var_Patch2(a5)
beq.s .AddPatch
move.l var_Patch2(a5),a0
cmp.w libp_Offset(a0),SF_OFFSET
bne.s .AddPatch
cmp.l a0,SF_NEWFUNC
beq.s .RemoveThisOne
cmp.l libp_OldFunc(a0),SF_NEWFUNC
beq.s .RemoveThisOne
move.l a0,var_Patch1(a5)
bra.s .RemoveLoop
.RemoveThisOne
move.l var_Patch1(a5),a0
move.l var_Patch2(a5),a1
move.l libp_NextPatch(a1),libp_NextPatch(a0)
; no need for Disable() for a single instruction
move.l libp_OldFunc(a1),libp_OldFunc(a0)
move.l libp_NewFunc(a1),SF_OLDFUNC
move.l #libp_SIZEOF,d0
bsr FreeMemory
bra .Done
.AddPatch
; let's make a new patch on this function
move.w SF_OFFSET,d0
move.l var_Patch(a5),a0
move.l libp_NextPatch(a0),a0
move.l SF_NEWFUNC,a1
bsr MakePatch
move.l d0,var_Patch1(a5)
beq .Done
move.l SF_NEWFUNC,a0
jsr _LVODisable(a6)
bsr OldSetFunction
move.l var_Patch1(a5),a0
move.l d0,libp_OldFunc(a0)
jsr _LVOEnable(a6)
tst.l d0
beq.s .OldSetFuncFailed2
move.l var_Patch(a5),a0
move.l var_Patch1(a5),libp_NextPatch(a0)
move.l var_Patch1(a5),SF_OLDFUNC
bra .Done
.OldSetFuncFailed2
move.l var_Patch1(a5),a1
move.l #libp_SIZEOF,d0
bsr FreeMemory
bra .Done
.NotThisPatch
move.l var_Patch1(a5),var_Patch(a5)
bra .NextPatch
.NoMoreLib
; create a new library header
move.l a0,var_HeadPtr(a5)
move.l SF_LIBRARY,a0
bsr MakeHead
move.l d0,var_HeadPtr1(a5)
beq.s .Done
move.l var_HeadPtr(a5),a0
move.l d0,libh_NextLib(a0)
move.w SF_OFFSET,d0
suba.l a0,a0
move.l SF_NEWFUNC,a1
bsr MakePatch
move.l d0,var_Patch(a5)
beq.s .CantMakePatch
move.w #MAGIC_OFFSET,d0
move.l var_Patch(a5),a0
sub.l a1,a1
bsr MakePatch
move.l var_HeadPtr1(a5),a0
move.l d0,libh_Patches(a0)
beq.s .NoMagic
move.l SF_NEWFUNC,a0
jsr _LVODisable(a6)
bsr OldSetFunction
move.l var_Patch(a5),a0
move.l d0,libp_OldFunc(a0)
jsr _LVOEnable(a6)
tst.l d0
beq.s .OldSetFuncFailed3
move.l var_HeadPtr(a5),a0
move.l var_HeadPtr1(a5),libh_NextLib(a0)
move.l var_Patch(a5),SF_OLDFUNC
bra.s .Done
.OldSetFuncFailed3
move.l var_HeadPtr1(a5),a1
move.l libh_Patches(a1),a1
move.l #libp_SIZEOF,d0
bsr.s FreeMemory
.NoMagic
move.l var_Patch(a5),a1
move.l #libp_SIZEOF,d0
bsr.s FreeMemory
.CantMakePatch
move.l var_HeadPtr1(a5),a1
move.l #libh_SIZEOF,d0
bsr.s FreeMemory
.Done lea var_SetFuncSemaphore(a5),a0
jsr _LVOReleaseSemaphore(a6)
jsr _LVOCacheClearU(a6)
move.l SF_OLDFUNC,d0
movem.l (sp)+,a0-a3/a5/d1/d4-d7
rts
; flag definitions for var_Flags
BITDEF SF,ALLOCATING,0
BITDEF SF,BLOCKAVAILABLE,1
AllocMemory ; (D0)Memory=AllocMemory(Size)(D0)
move.l var_MemoryPool(a5),a0
jsr _LVOForbid(a6)
bset #SFB_ALLOCATING,var_Flags(a5)
jsr _LVOAllocPooled(a6)
bclr #SFB_ALLOCATING,var_Flags(a5)
jsr _LVOPermit(a6)
btst #SFB_BLOCKAVAILABLE,var_Flags(a5)
bne.s .NoNeed
move.l d0,-(sp)
beq.s .Nope
move.l #EMERGENCY_BLOCKSIZE,d0
move.l #MEMF_PUBLIC,d1
jsr _LVOAllocMem(a6)
move.l d0,var_EmergencyBlock(a5)
beq.s .Nope
bset #SFB_BLOCKAVAILABLE,var_Flags(a5)
.Nope move.l (sp)+,d0
.NoNeed rts
FreeMemory ; FreeMemory(Memory,Size)(A1,D0)
move.l var_MemoryPool(a5),a0
jmp _LVOFreePooled(a6)
LowMemHandler.Entry
move.b var_Flags(a1),d0
and.b #SFF_ALLOCATING!SFF_BLOCKAVAILABLE,d0
beq.s .DoNothing
cmp.w #EMERGENCY_BLOCKSIZE,memh_RequestSize(a0)
bgt.s .DoNothing
bclr #SFB_BLOCKAVAILABLE,var_Flags(a1)
move.l var_EmergencyBlock(a1),a1
move.l #EMERGENCY_BLOCKSIZE,d0
jsr _LVOFreeMem(a6)
move.l #MEM_ALL_DONE,d0
rts
.DoNothing
move.l #MEM_DID_NOTHING,d0
rts
LowMemHandler.Interrupt
dc.l 0,0
dc.b NT_INTERRUPT,-120
dc.l LowMemHandler.Name
dc.l Variables ; IS_DATA field
dc.l LowMemHandler.Entry
Semaphore.Name dc.b 'SafeFunction.Semaphore',0
LowMemHandler.Name dc.b 'SafeFunction.EmergencyHandler',0
section Variables,BSS
rsreset ; these variables are protected by the semaphore
var_HeadPtr rs.l 1
var_HeadPtr1 rs.l 1
var_Patch rs.l 1
var_Patch1 rs.l 1
var_Patch2 rs.l 1
var_MainHeader rs.l 1 ; first LibHeader
var_OldSetFunc rs.l 1 ; points to real SetFunction()
var_SetFuncSemaphore rs.b SS_SIZE
var_EmergencyBlock rs.l 1
var_MemoryPool rs.l 1
var_Flags rs.b 1
var_Flags1 rs.b 1 ; word-alignment, for now...
var_SIZEOF rs.w 0
Variables ds.b var_SIZEOF